﻿using System;
using System.Collections.Generic;
using System.Linq;
using Neo4jConnect.Rest;
using Neo4jConnect.Traversal;
using Newtonsoft.Json.Linq;

namespace Neo4jConnect
{
    public class GraphDatabaseService : IGraphDatabaseService
    {
        private readonly IRestService restService;

        public IRestService RestService
        {
            get { return restService; }
        }

        public GraphDatabaseService(string baseUri) : this(new HttpClientRestService(baseUri))
        {
        }

        public GraphDatabaseService(Uri baseUri) : this(new HttpClientRestService(baseUri))
        {
        }

        public GraphDatabaseService(IRestService resetService)
        {
            this.restService = resetService;
        }

        public RootNode GetRootNode()
        {
            var json = restService.Node.GetRoot();
            RootNode node = new RootNode();
            node.Init(this);
            node.ReadJson(JObject.Parse(json));
            return node;
        }

        public T CreateNode<T>() where T : Node, new()
        {            
            T node = new T();
            node.Init(this);        
            return node;
        }

        internal void CreateNode(Node node)
        {
            var obj = new JObject();
            foreach (KeyValuePair<string, string> pair in node.GetProperties())
                obj.Add(new JProperty(pair.Key, pair.Value));

            var nodeData = restService.Node.Create(obj.ToString());
            node.ReadJson(JObject.Parse(nodeData));            
        }

        internal void DeleteNode(ulong nodeId)
        {
            restService.Node.Delete(nodeId);            
        }

        public T GetNode<T>(ulong nodeId) where T : Node, new()
        {
            var json = restService.Node.Get(nodeId);
            T node = new T();
            node.Init(this);
            node.ReadJson(JObject.Parse(json));            
            return node;
        }

        internal IEnumerable<Relationship> GetRelationshipsOnNode(ulong id)
        {
            var json = restService.Relationship.GetRelationshipsOnNode(id, RelationshipDirection.All,  Enumerable.Empty<string>());

            foreach (JObject jsonRelationship in JArray.Parse(json).Cast<JObject>())
            {
                Relationship relationship = new Relationship();
                relationship.Init(this);
                relationship.ReadJson(jsonRelationship);                
                yield return relationship;
            }
        }

        internal void SetNodeProperties<T>(T node) where T : Node
        {
            var obj = new JObject();
            foreach (KeyValuePair<string, string> pair in node.GetProperties())
                obj.Add(new JProperty(pair.Key, pair.Value));

            restService.Node.SetProperties(node.Id, obj.ToString());         
        }

        internal void SetRelationshipProperties<T>(T relationship) where T : Relationship
        {
            var obj = new JObject();
            foreach (KeyValuePair<string, string> pair in relationship.GetProperties())
                obj.Add(new JProperty(pair.Key, pair.Value));

            restService.Relationship.SetProperties(relationship.Id, obj.ToString());
        }

        public void DeleteRelationship(ulong relationshipId)
        {
            restService.Relationship.Delete(relationshipId);
        }

        public Relationship CreateRelationship<T>(T fromNode, string relationshipType, T toNode)  where T : Node
        {            
            var jsonObject = new JObject();
            jsonObject.Add(new JProperty("to", toNode.SelfUri.AbsolutePath));
            jsonObject.Add(new JProperty("data", new JObject()));
            jsonObject.Add(new JProperty("type", relationshipType));
                
            var json = restService.Relationship.Create(fromNode.Id, jsonObject.ToString());
            
            Relationship relationship = new Relationship();
            relationship.Init(this);
            relationship.ReadJson(JToken.Parse(json));
            return relationship;
        }

        public IEnumerable<Node> Traverse(ulong nodeId, TraversalBuilder builder)
        {                        
            var json = restService.Traversal.Traverse(nodeId, TraversalReturnType.Node, builder.Build());

            foreach (JObject jsonRelationship in JArray.Parse(json).Cast<JObject>())
            {
                Node node = new Node();
                node.Init(this);
                node.ReadJson(jsonRelationship);                
                yield return node;
            }
        }
    }       
}
